<?php
/**
 * CController class file.
 * 控制器基类.
 * @author Huan Song <iphper@yeah.net>
 * @version $Id: CController.php 1 2011-02-28 10:18:16Z huan.song $
 * @package system.controller
 * @since 2.0
 */
abstract class CController extends CApplication
{
	/**
	 * @var Object CView.
	 */
	private $_view = null;
	
	/**
	 * @var Object CRequest.
	 */
	private $_request = null;
	
	/**
	 * 构造方法: 初始化request
	 */
	public function __construct()
	{
		$this->setRequest(CRequest::getInstance());
		$this->_init();
	}
	
	/**
     * 跳转到另一个URL地址.
     * <pre>
	 * $this->forward(array('t' => ''));
	 * $this->forward(array('t' => '请登录'), 'login');
	 * $this->forward(array('t' => '请登录'), 'login', 'users');
	 * $this->forward(array('t' => '请登录'), 'login', 'users', 'administrator.php');
	 * </pre>
	 * @param array|null $params URL的参数.
     * @param string|null $action 跳转到的方法. 如果方法为null, 则取本控制器方法.
     * @param string|null $controller 跳转到的控制器. 如果控制器为null, 则取本控制器.
     * @param string $url URL地址. 如果不是一个字符串, 则取baseUrl
     * @param int $delay 跳转延迟时间.
     */
    public function forward($params = array(), $action = null, $controller = null, $url = null)
    {
        $url = $this->getRequest()->createUrl($params, $action, $controller, $url);
        $this->redirect($url);
    }
	
    /**
     * 跳转到另一个URL地址.
     * <pre>
	 * $this->redirect('index.php?r=users/login');
	 * $this->redirect('index.php?r=users/login', '页面正在跳转中, 请等待');
	 * $this->redirect('index.php?r=users/login', '页面正在跳转中, 请等待', 3);
	 * </pre>
     * @param string $url 跳转到的URL.
     * @param string $message 跳转延迟时, 页面显示的消息.
     * @param int $delay 跳转延迟时间.
     */
    public function redirect($url, $message = '', $delay = 0)
    {
        $delay = (int) $delay;
		if (headers_sent()) 
		{
			$html = "<meta http-equiv='refresh' content='{$delay}; url={$url}'>";
			if ($delay > 0)
				$html .= $message;
			
			echo $html;
		}
		else 
		{
			if ($delay > 0) 
			{
				header("refresh:{$delay}; url={$url}");
				echo $message;
			}
			else 
				header('location: ' . $url);
		}
		exit;
    }
	
	/**
	 * 子类构造方法: 为了防止构造方法被重写, 子类可以调用此方法作为构造方法.
	 */
	protected function _init()
	{
	}
	
	/**
	 * 获取模板引擎.
	 * <pre>
	 * $view = $this->getView();
	 * $view = $this->view.
	 * </pre>
     * return Object CView.
     */
	public function getView()
	{
		if (null === $this->_view)
			$this->setView();
		
		return $this->_view;
	}
	
	/**
     * 设置 Object CView类.
     * <pre>
     * $view = null;
	 * $this->setView($view);
	 * 如果$view === null, 取配置中
	 * 'view' => array(
	 * 		'classPath' => FRAMEWORK_DIR . '/views/CView.php', //模板引擎类路径.
	 * 	),
	 * 结果: $this->_view = new CView();
	 * </pre>
     * @param Object $view instanceof IView.
     * @throws CViewException. 如果模板引擎类没有实现IView接口, 抛出异常.
     */
	public function setView(IView $view = null)
	{
		if (null === $view)
		{
			$classPath = CViewConfig::getClassPath();
			require_once $classPath;
			$basename = basename($classPath);
			$view = substr($basename, 0, strpos($basename, '.'));
			if (!class_exists($view))
				throw new CViewException('CController:setView CView not exists', array('{CView}' => $view));
			
			$view = new $view();
		}
		
		if (!$view instanceof IView)
			throw new CViewException('CController:setView CView must instanceof IView');
		
		$this->_view = $view;
		$this->initView();
	}
	
	/**
     * 初始化 Object CView类, 设置常用路径.
     * <pre>
	 * $this->initView();
	 * </pre>
     */
	public function initView()
	{
		$view = $this->getView();
		$request = $this->getRequest();
		// 页面编码.
		$view->charset = CViewConfig::getCharset();
		// 网站根路径.
		$view->baseUrl = $request->baseUrl;
		$view->bUrl = $request->createUrl(array(), '', '');
		$view->cUrl = $request->createUrl(array(), '');
		$view->aUrl = $request->createUrl();
		
		// URL根目录.
		$view->basePath = $this->getRequest()->basePath;
		// 公共文件夹根目录.
		$view->publicPath = $view->basePath . '/public';
		$this->postInitView();
	}
	
	/**
	 * 调用initView方法后调用.
	 * 子类可以重写此方法.
	 */ 
	public function postInitView()
	{
	}
	
	/**
     * return Object CRequest.
     */
	public function getRequest()
	{
		return $this->_request;
	}
	
	/**
     * 设置 Object CRequest类.
     * @param CRequest $request.
     */
	public function setRequest(CRequest $request)
	{
		$this->_request = $request;
	}
	
	/**
     * 解析并显示模板.
     * @param string|null $tpl 模板路径.
     * @return void.
     */
	public function display($tpl = null)
	{
		$this->preDisplay();
		
		if (null === $tpl) 
			$tpl = CControllerConfig::getController() . DS . CControllerConfig::getAction() . CViewConfig::getTemplateSuffix();
		
		$this->getView()->display($tpl);
	}
	
	/**
     * 解析并显示模板前调用的方法.
     * 子类可以重写此方法.
     * @return void.
     */
	public function preDisplay()
	{
	}
	
	/**
     * 页面请求时错误处理.
     * 'controller' => array(
     * 		'errorAction' => 'index/error',
	 * 	),
	 * 解析配置文件中上面这段代码, 可以获取错误处理类和方法
	 * $errorAction = CControllerConfig::getErrorAction();
	 * $errorAction = array('IndexController' => 'errorAction');
     * set_exception_handler($errorAction);
     * @param integer $code 错误码.
     * @param string $message 错误消息.
     * @return void.
     */
	public function errorAction(Exception $e)
	{
		$code = $e->getCode();
		$message = $e->getMessage();
		$file = in_array($code, array(400, 401, 403, 404, 500, 503)) ? 'error' . $code : 'exception';
		include CViewConfig::getLanguageDir() . DS . $file . '.php';
	}
	
	/**
	 * 调用指定Action前调用的方法.
	 * 此方法在CDispatcher类run方法中已经调用.
	 * $class->preAction();
	 * 子类可以重写此方法.
	 */ 
	public function preAction()
	{
	}
	
	/**
	 * 调用指定Action后调用的方法.
	 * 此方法在CDispatcher类run方法中已经调用.
	 * $class->postAction();
	 * Action中加上exit(), 也不会执行此方法.
	 * 子类可以重写此方法.
	 */
	public function postAction()
	{
		if (!CViewConfig::getNoView())
			$this->display();
	}
}
